#include "sys.h"
#include "net.h"
#include "log.h"
#include "dial.h"

void* do_tcp(void* arg)
{
    int fd;
    int ret = 0, is_timeout = 0;
    char buf[128] = { 0 };
    fd_set wrfds;
    struct timeval tmo;
    struct sockaddr_in peer_addr;
    struct timeval s_tm, e_tm;
    struct addrinfo* answer;
    float diff_time;
    conn_track ct = { 0 };
    dial_arg_t* dial_arg = (dial_arg_t*)arg;
    dial_result_t* dial_result = calloc(1, sizeof(dial_result_t));
    tcp_arg_t* tcp_arg = &dial_arg->in.tcp_arg;
    tcp_result_t* tcp_result = &dial_result->out.tcp_result;

    if (dial_arg->bridge_mode) {
        ct.dest_ip = tcp_arg->ip;
        ct.dest_port = tcp_arg->port;
        ct.proto = IPPROTO_TCP;
        fd = conn_track_regist(&ct);
    } else {
        fd = socket(AF_INET, SOCK_STREAM, 0);
        if (fd < 0) {
            log_syserr("socket error.");
            dial_result->err = TCP_ERR_SOCKET;
            goto out;
        }
        peer_addr.sin_family = AF_INET;
        peer_addr.sin_addr.s_addr = tcp_arg->ip;
        peer_addr.sin_port = tcp_arg->port;
        setnonblocking(fd);
    }

    gettimeofday(&s_tm, NULL);
    if (dial_arg->bridge_mode) {
        if (conn_tcp_connect(&ct, fd) < 0) {
            log_error("tcp connect error.");
            dial_result->err = TCP_ERR_CONNECT;
            goto out;
        }
    } else {

        while (1) {
            FD_ZERO(&wrfds);
            FD_SET(fd, &wrfds);
            tmo.tv_sec = tcp_arg->timeout ? tcp_arg->timeout : 2;
            tmo.tv_usec = 0;
            ret = select(fd + 1, NULL, &wrfds, NULL, &tmo);
            if (ret == 0) {
                is_timeout = 1;
                dial_result->err = TCP_ERR_TIMEOUT;
                break;
            } else if (ret > 0) {
                if (connect(fd, (struct sockaddr*)&peer_addr, sizeof(peer_addr)) < 0) {
                    if (errno != EINPROGRESS) {
                        log_syserr("connect error.");
                        dial_result->err = TCP_ERR_CONNECT;
                        goto out;
                    }
                    continue;
                }
                break;
            }
        }
    }
    gettimeofday(&e_tm, NULL);
    diff_time = TV2MS(&e_tm) - TV2MS(&s_tm);
    tcp_result->latency =  diff_time;
    tcp_result->connected = is_timeout ? 0 : 1;
out:
    if(fd > 0)
        close(fd);
    return (void*)dial_result;
}

void tcp_result_print(tcp_result_t* result)
{
    log_trace("TCP latency:%.2f", result->latency);
}